package com.zhiren.formular;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;
import java.util.Map.Entry;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import com.zhiren.common.CustomMaths;
import com.zhiren.common.StringUtils;
/**
 * 
 * @author Ray.
 *
 */
public class Calculator {
	
	private static final int timeStep_second = 1;
	private static final int timeStep_minite = 1*60;
	private static final int timeStep_hour = 1*60*60;
	private static final int timeStep_day = 1*60*60*24;

	public static final String REG_PATTERN_IF = "[i|I][f|F]\\([^,]*,[^,]*,[^,]*\\)";
	public static final String REG_PATTERN_ROUND = "[r|R][o|O][u|U][n|N][d|D]\\((?![^,]*[r|R][o|O][u|U][n|N][d|D])[^,]*,[\\s]*[\\d]{1,2}\\)";
	public static final String REG_PATTERN_ROUNDNEW = "[r|R][o|O][u|U][n|N][d|D]_[n|N][e|E][w|W]\\((?![^,]*[r|R][o|O][u|U][n|N][d|D]_[n|N][e|E][w|W])[^,]*,[\\s]*[\\d]{1,2}\\)";
	public static final String REG_PATTERN_DECODE = "[d|D][e|E][c|C][o|O][d|D][e|E]\\((?![^,]*[d|D][e|E][c|C][o|O][d|D][e|E])[^,]*,([0-9]|[,]|[\\.]|[\\s])*\\)";
	public static final String REG_PATTERN_CEIL = "[c|C][e|E][i|I][l|L]\\((?![^,]*[c|C][e|E][i|I][l|L])[^,]*\\)";
	public static final String REG_PATTERN_FLOOR = "[f|F][l|L][o|O][o|O][r|R]\\((?![^,]*[f|F][l|L][o|O][o|O][r|R])[^,]*\\)";
	public static final String REG_PATTERN_ABS = "[a|A][b|B][s|S]\\((?![^,]*[a|A][b|B][s|S])[^,]*\\)";

	public static String[] funcArray = {"round", "decode", "round_new", "if", "ceil", "floor", "abs", "subsecond", "subminite", "subhour", "subday"};
	
	public static BigDecimal ABS(String streval) throws Exception{
		if(streval == null || streval.equals("")){
    		throw new Exception("公式abs("+streval+")中,内容为空");
    	}
		BigDecimal v = BigDecimal.ZERO;
		v = Calcstra.getCalcstraValue(streval).abs();
		return v;
	}
	
	public static Object doEval(String strEval) throws ScriptException{
		ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); 
		return engine.eval(strEval);
	}
	
	public static BigDecimal IF(String streval) throws Exception{
		if(streval == null || streval.equals("")){
    		throw new Exception("公式if("+streval+"),内容为空");
    	}
		BigDecimal v = BigDecimal.ZERO;
		String eval[] = streval.split(",");
		if((Boolean)doEval(eval[0])){
			v = Calcstra.getCalcstraValue(eval[1]);
		}else{
			v = Calcstra.getCalcstraValue(eval[2]);
		}
		return v;
	}
	
	public static BigDecimal DECODE(String streval) throws Exception{
		if(streval == null || streval.equals("")){
    		throw new Exception("公式decode("+streval+"),内容为空");
    	}
		BigDecimal v = BigDecimal.ZERO;
		String eval[] = streval.split(",");
		for(int i=0; i< eval.length; i++){
			if(Double.valueOf(eval[0]) == Double.valueOf(eval[i++])){
				v = new BigDecimal(eval[i]);//Double.valueOf(eval[i]);
			}
		}
		return v;
	}
	
	public static BigDecimal FLOOR(String streval) throws Exception{
		if(streval == null || streval.equals("")){
    		throw new Exception("公式floor("+streval+"),内容为空");
    	}
		BigDecimal v = BigDecimal.ZERO;
		String eval[] = streval.split(",");
		if(eval.length>1){
			throw new Exception("公式floor("+streval+")错误");
		}
		v = Calcstra.getCalcstraValue(eval[0]).setScale(0, BigDecimal.ROUND_FLOOR);
		return v;
	}
	
	public static BigDecimal CEIL(String streval) throws Exception{
		if(streval == null || streval.equals("")){
    		throw new Exception("公式ceil("+streval+"),内容为空");
    	}
		BigDecimal v = BigDecimal.ZERO;
		String eval[] = streval.split(",");
		if(eval.length>1){
			throw new Exception("公式ceil("+streval+")错误");
		}
		v = Calcstra.getCalcstraValue(eval[0]).setScale(0, BigDecimal.ROUND_CEILING);
		return v;
	}
	
	public static BigDecimal ROUND_NEW(String streval) throws Exception{
		if(streval == null || streval.equals("")){
    		throw new Exception("公式round_new("+streval+"),内容为空");
    	}
		BigDecimal v = BigDecimal.ZERO;
		String eval[] = streval.split(",");
		if(eval.length>2){
			throw new Exception("公式round_new("+streval+")错误");
		}
		v = Calcstra.getCalcstraValue(eval[0]).setScale(Integer.parseInt(eval[1]), BigDecimal.ROUND_HALF_EVEN);
		return v;
	}
	
	public static BigDecimal ROUND(String streval) throws Exception{
		if(streval == null || streval.equals("")){
    		throw new Exception("公式round("+streval+"),内容为空");
    	}
		BigDecimal v = BigDecimal.ZERO;
		String eval[] = streval.split(",");
		if(eval.length>2){
			throw new Exception("公式round("+streval+")错误");
		}
		v = Calcstra.getCalcstraValue(eval[0]).setScale(Integer.parseInt(eval[1]),BigDecimal.ROUND_HALF_UP);
		return v;
	}
	
	public static BigDecimal SUBSECOND(String streval) throws Exception{
		BigDecimal v = BigDecimal.ZERO;
		v = subTime(streval, timeStep_second);
		return v;
	}
	
	public static BigDecimal SUBMINITE(String streval) throws Exception{
		BigDecimal v = BigDecimal.ZERO;
		v = subTime(streval, timeStep_minite);
		return v;
	}
	
	public static BigDecimal SUBHOUR(String streval) throws Exception{
		BigDecimal v = BigDecimal.ZERO;
		v = subTime(streval, timeStep_hour);
		return v;
	}
	
	public static BigDecimal SUBDAY(String streval) throws Exception{
		BigDecimal v = BigDecimal.ZERO;
		v = subTime(streval, timeStep_day);
		return v;
	}
	
	private static BigDecimal subTime(String streval, Integer timeType) throws Exception{
		String strevalname = "";
		switch(timeType){
			case timeStep_second: strevalname = "second"; break;
			case timeStep_minite: strevalname = "minite"; break;
			case timeStep_hour: strevalname = "hour"; break;
			case timeStep_day: strevalname = "day"; break;
			default :break;
		}
		if(streval == null || streval.equals("")){
    		throw new Exception("公式sub"+strevalname+"("+streval+"),内容为空");
    	}
		String eval[] = streval.split(",");
		if(eval.length>2){
			throw new Exception("公式sub"+strevalname+"("+streval+")错误");
		}
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date dmin = df.parse(eval[0]);
        Date dmax = df.parse(eval[1]);
        if(dmin.after(dmax)){
        	Date dtmp = dmax;
        	dmax = dmin;
        	dmin = dtmp;
        }
        BigDecimal bdmin = BigDecimal.valueOf(dmin.getTime());
        BigDecimal bdmax = BigDecimal.valueOf(dmax.getTime());
        BigDecimal bdsec = bdmax.subtract(bdmin).divide(BigDecimal.valueOf(1000));
		//double sec = BigDecimal.valueOf((dmax.getTime() - dmin.getTime())/1000);
		BigDecimal bdval = bdsec.divide(BigDecimal.valueOf(timeType));
		//double val = sec/timeType;
		return bdval;
	}
	
	public static String replaceFunc(String strEval) throws Exception{
		if(strEval == null || strEval.equals("")){
    		throw new Exception("表达式为空");
    	}
		String strtmp = strEval.toLowerCase();
		
		strtmp = strtmp.replaceAll("（", "(").replaceAll("）", ")").replaceAll("，", ",");
//    	System.out.println(strtmp);
    	Stack<String> stack = putFunc2stack(strtmp);
//    	System.out.println(stack);
    	while(!stack.isEmpty()){
    		String funcName = stack.pop();
//    		System.out.println(funcName);
    		strtmp = setFuncValue(strtmp, funcName);
//    		System.out.println(strtmp);
    	}
    	return strtmp;
	}

	public static BigDecimal getCalculatorValueBD(String strEval) throws Exception{
		if(strEval == null || strEval.equals("")){
			throw new Exception("表达式为空");
		}
		String strtmp = strEval.toLowerCase();
		strtmp = strtmp.replaceAll("（", "(").replaceAll("）", ")").replaceAll("，", ",");
		Stack<String> stack = putFunc2stack(strtmp);
		while(!stack.isEmpty()){
			String funcName = stack.pop();
			strtmp = setFuncValue(strtmp, funcName);
		}
		return Calcstra.getCalcstraValue(strtmp);
	}
    
    public static double getCalculatorValue(String strEval) throws Exception{
    	if(strEval == null || strEval.equals("")){
    		throw new Exception("表达式为空");
    	}
    	String strtmp = strEval.toLowerCase();
    	
    	strtmp = strtmp.replaceAll("（", "(").replaceAll("）", ")").replaceAll("，", ",");
//    	System.out.println(strtmp);
    	Stack<String> stack = putFunc2stack(strtmp);
//    	System.out.println(stack);
    	while(!stack.isEmpty()){
    		String funcName = stack.pop();
//    		System.out.println(funcName);
    		strtmp = setFuncValue(strtmp, funcName);
//    		System.out.println(strtmp);
    	}
    	return Calcstra.getCalcstraValue(strtmp).doubleValue();
    }
    
    private static String setFuncValue(String streval, String strfuncName) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException{
    	String strfunc = getFunc(streval, strfuncName);
//    	System.out.println(strfunc);
    	String strbody = getFuncBody(strfunc, strfuncName);
//    	System.out.println(strbody);
    	Method mtd = Class.forName(Calculator.class.getName()).getMethod(strfuncName.toUpperCase(), java.lang.String.class);
    	Object o = mtd.invoke(strfuncName.toUpperCase(), strbody);
    	//streval.replace(strfunc, String.valueOf(o)); 原replace方法有可能替换同一函数，造成函数体为空的bug
    	return StringUtils.replaceLast(streval, strfunc, String.valueOf(o));
    }
    
    private static String getFunc(String streval, String strfuncName){
    	if(streval == null || streval.equals("")){
    		return null;
    	}
    	String strtmp = streval.toLowerCase();
    	Stack<Character> stack = new Stack<Character>();
    	String strfunc = "";
    	int index = strtmp.lastIndexOf(strfuncName);
    	if(index != -1){
    		//strtmp = strtmp.substring(index + strfuncName.length() +1);
    		for(int i = index ; i < strtmp.length() ;i++){
    			char c = strtmp.charAt(i);
    			strfunc += c;
    			if(c=='('){
    				stack.push(c);	
    			}
    			if(c==')'){
    				stack.pop();
    				if(stack.isEmpty()){
    					break;
    				}
    			}
    		}
    	}else{
    		return null;
    	}
    	return strfunc;
    }
    
    private static String getFuncBody(String streval, String strfuncName){
    	if(streval == null || streval.equals("")){
    		return null;
    	}
    	String strtmp = streval.toLowerCase();
    	Stack<Character> stack = new Stack<Character>();
    	String strBody = "";
    	int index = strtmp.lastIndexOf(strfuncName);
    	if(index != -1){
    		for(int i = index + strfuncName.length() +1 ; i < strtmp.length() ;i++){
    			char c = strtmp.charAt(i);
    			if(c=='('){
    				stack.push(c);	
    			}
    			if(c==')'){
    				if(stack.isEmpty()){
    					break;
    				}else{
    					stack.pop();
    				}
    			}
    			strBody += c;
    		}
    	}else{
    		return null;
    	}
    	return strBody;
    }
	
    private static Stack<String> putFunc2stack(String streval){
    	if(streval == null || streval.equals("")){
    		return null;
    	}
    	String strtmp = streval.toLowerCase();
    	Stack<String> stack = new Stack<String>();
    	int count = 0;
    	while(true){
    		int fromIndex = -1;
    		String funcName = "";
	    	for(int i =0; i <funcArray.length ; i++){
	    		int index = strtmp.indexOf(funcArray[i]);
	    		
	    		if(index != -1){
	    			if(index!=0){
	    				char previous = strtmp.toCharArray()[index-1];
	    				
	    				if(Character.isLetter(previous) || previous=='_'){
			    			continue;
			    		}
	    			}
	    			if(index + funcArray[i].length()+1 <= strtmp.length()){
	    				char next = strtmp.toCharArray()[index + funcArray[i].length()];
	    				if(Character.isLetter(next) || next=='_' || next!='('){
			    			continue;
			    		}
	    			}
	    			if(fromIndex == -1){
	    				fromIndex = index;
	    				funcName = funcArray[i];
	    			}else{
	    				if(index < fromIndex){
	    					fromIndex = index;
		    				funcName = funcArray[i];
	    				}
	    			}
	    		}
	    	}
	    	if(fromIndex == -1){
	    		break;
	    	}else{
	    		strtmp = strtmp.substring(strtmp.indexOf(funcName)+funcName.length());
	    		stack.push(funcName);
	    	}
	    	if(count++ > 10000){
	    		break;
	    	}
    	}
		return stack;
    }
    
    /**
	 * 
	 * @param conditionEval	表达式
	 * @param val			值
	 * @return
     * @throws Exception 
	 */
	public static boolean judgeCondition(String conditionEval, Map<String,String> val) throws Exception{
		if(conditionEval==null||"".equals(conditionEval)){
			return false;
		}
		String streval = conditionEval;
		ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); 
		Iterator<Entry<String, String>> it = val.entrySet().iterator();
		while(it.hasNext()){
			Entry<String, String> e = (Entry<String, String>)it.next();
			engine.put(e.getKey(), val.get(e.getKey()));
			streval = streval.replaceAll(e.getKey(), val.get(e.getKey()));
			streval = replaceFunc(streval);
		}
		return Boolean.parseBoolean(String.valueOf(engine.eval(streval)));
	}
}
